Skip to main content
Version: 2.0

Upload Media For Message

Objective

To upload media files directly onto Meta servers — either via a publicly accessible URL or from a local file — without requiring a permanent public hosting URL.

Benefits:

  • Better security since files do not need to be hosted on a public URL
  • Eliminates dependency on external hosting

Before You Start

This guide assumes you have read the Before You Start section.

You will need:

  • A user access token

Authentication

Media Upload APIs are accessed using a Bearer token. Use the endpoint below to generate one.

Request Type: POST

UAT Endpoint: https://apiuat.aclwhatsapp.com/auth/realms/ipmessaging/protocol/openid-connect/token

curl --location \
'https://apiuat.aclwhatsapp.com/auth/realms/ipmessaging/protocol/openid-connect/token' \
--header 'cache-control: no-cache' \
--header 'content-type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=ipmessaging-client' \
--data-urlencode 'username=username' \
--data-urlencode 'password=password'

Header Values

NameValue
cache-controlno-cache
content-typeapplication/x-www-form-urlencoded

Data Object Values

NameDescriptionValue
grant_typeAuthentication methodpassword
usernameProvided by Sinch at onboarding
client_idFixed valueipmessaging-client
passwordProvided by Sinch at onboarding

Response

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAi",
"expires_in": 300,
"token_type": "Bearer",
"not-before-policy": 0,
"session_state": "f2cd48bf-d162-4754-b2c7-bdc9c97064b3",
"scope": "email profile"
}
NameDescription
access_tokenToken to be used in subsequent API calls
expires_inToken validity in seconds
token_typeFixed value — Bearer

Media Upload

The media upload for messages is a 2-step process. The media must be pre-uploaded to Meta servers before being used in carousel or media ID messages.


Step 1 — Upload Media & Get Acknowledgement ID

Send a POST request to upload your media. You can upload via a public URL or directly from a local file.


Method A — Upload via Public URL

UAT Endpoint: https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media/upload

Production Endpoint: https://api.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media/upload

curl --location \
'https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/177384039855054/media/upload' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <token>' \
--data '{
"mediaUrl": "https://download.samplelib.com/mp3/sample-3s.mp3",
"mediaFormat": "audio/mpeg",
"always_upload": false
}'

Header Values

NameValue
Content-Typeapplication/json
AuthorizationBearer token

Data Object Values

NameDescriptionValue
mediaFormatMIME type of the media as supported by Metae.g. application/pdf
mediaUrlPublicly accessible media URL
always_uploadRe-upload even if a mediaId already existstrue / false

Method B — Upload from Local File

Use this method to upload a media file directly from your local system without requiring a public URL.

UAT Endpoint: https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media/file/upload

Production Endpoint: https://api.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media/file/upload

curl --location \
'https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/<waba_id>/media/file/upload' \
--header 'Authorization: Bearer <token>' \
--form 'mediaFormat="image/png"' \
--form 'always_upload="true"' \
--form 'file=@"/<path-to-local-file>"'

Header Values

NameValue
AuthorizationBearer token

Data Object Values

NameDescriptionValue
mediaFormatMIME type of the media as supported by Metae.g. application/pdf
always_uploadRe-upload even if a mediaId already existstrue / false
filePath to the local filee.g. /home/user/image.png

Response (both methods)

{
"acknowledgementId": "1752915620015378432",
"acceptedTime": 1706762626066
}

Error & Exceptions

  1. Duplicate Request (without always_upload) — Multiple requests for the same file with always_upload: false.
{
"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2",
"acknowledgementId": "1752915620015378432",
"error": "duplicate.media.upload.request"
}
  1. Duplicate Request (with always_upload) — Multiple requests for the same file with always_upload: true, but the existing media was uploaded within the last 7 days.
{
"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2",
"acknowledgementId": "1752915620015378432",
"error": "duplicate.media.upload.request"
}

If the media exists but was uploaded more than 7 days ago, the system will allow re-upload and generate a new acknowledgement ID.

  1. Rate Limit Error — Enterprises can upload a maximum of 5 media files per minute. Maximum file size is 20 MB. Exceeding either limit returns the following errors:
{
"error": "request.limit.exceeded"
}
{
"error": "File size limit exceeds"
}

Step 2 — Fetch Media ID using Acknowledgement ID

After receiving the acknowledgement ID, use the request below to retrieve the Media ID required for sending WA messages.

UAT Endpoint: https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media

Production Endpoint: https://api.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media

Request Type: POST

curl --location \
'https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{"acknowledgementId": "<acknowledgementId>"}'

Header Values

NameValue
Content-Typeapplication/json
AuthorizationBearer token

Data Object Values

NameDescriptionValue
acknowledgementIdID generated in Step 11752915620015378432

Response

{
"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2",
"acknowledgementId": "1752915620015378432",
"mediaUrl": "https://download.samplelib.com/mp3/sample-3s.mp3",
"expiryTime": 1707972226000,
"status": "success"
}

Response Details

FieldDescription
mediaIdUse this to send the media message
acknowledgementIdProvided in the request
mediaUrlThe URL of the uploaded media
expiryTimeTimestamp until which media is available on Meta servers. Re-upload required after expiry.

Error & Exceptions

  1. Upload In Progress — Media upload is still being processed at Meta servers.
{
"acknowledgementId": "1755474606652657662",
"mediaUrl": "",
"expiryTime": null,
"status": "media.generation.in.progress",
"errors": [
{
"desc": "Media upload is in progress"
}
]
}
  1. Media Not Found — Media has expired.
{
"acknowledgementId": "1755473752088383468",
"status": "media.not.found"
}
  1. Media Generation Failed — Upload failed at Meta servers.
{
"acknowledgementId": "1755477593324261376",
"mediaUrl": "",
"expiryTime": null,
"status": "media.generation.failed",
"errors": [
{
"code": "502",
"desc": "Something went wrong while generating mediaId, please try again"
}
]
}

Step 3 — Fetch Media Details by Media ID

Retrieve media details using the mediaId to check the expiry timestamp and plan re-uploads before expiry.

UAT Endpoint: https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media

Production Endpoint: https://api.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media

Request Type: POST

curl --location \
'https://apiuat.aclwhatsapp.com/access-api/api/v1/wa/{waba-id}/media' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2"}'

Header Values

NameValue
Content-Typeapplication/json
AuthorizationBearer token

Data Object Values

NameDescriptionValue
mediaIdMedia ID generated after upload6fba7ef1-f845-4063-b96a-f814058213f2

Response

{
"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2",
"acknowledgementId": "1752915620015378432",
"mediaUrl": "https://download.samplelib.com/mp3/sample-3s.mp3",
"expiryTime": 1707972226000,
"status": "success"
}

Response Details

FieldDescription
mediaIdMedia ID of the uploaded media
acknowledgementIdAcknowledgement ID from the original upload request
mediaUrlThe URL of the uploaded media
expiryTimeTimestamp until which media is available on Meta servers. Re-upload required after expiry.

Error & Exceptions

  1. Media Not Found — Media has expired.
{
"mediaId": "6fba7ef1-f845-4063-b96a-f814058213f2",
"status": "media.not.found"
}